package com.jltx.storage.coroutinesdownloader.net

import com.jltx.storage.coroutinesdownloader.BaseApiService
import com.jltx.storage.coroutinesdownloader.RequestException
import com.jltx.storage.coroutinesdownloader.RequestResult
import com.jltx.storage.coroutinesdownloader.RequestStatus
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.channels.produce
import kotlinx.coroutines.isActive
import retrofit2.await
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import kotlin.coroutines.CoroutineContext

/**
 * @author jltxseo
 *         Created by junlintianxia on 2020/04/15.
 */
class DownloadFileMultiCall : CoroutineScope {
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.IO

    fun convertResponseBodyToRequestResult(
        url: String?,
        localPath: String?,
        apiService: BaseApiService?
    ): ReceiveChannel<RequestResult> = produce {
        if (url == null || localPath == null) {
            sendProducerResult(
                localPath ?: "null", url ?: "null", RequestStatus.STATUS_FAIL,
                RequestException(
                    RequestException.CODE_ERROR_IO_EXCEPTION,
                    "url or local path error"
                )
            )
            close()
            return@produce
        }
        var inputStream: InputStream? = null
        var outputStream: OutputStream? = null
        //文件总长度
        var totalSize = 0L
        //当前已下载长度
        var progressSize = 0L
        try {
            val responseBody = apiService?.downloadFile(url)?.await()
            //判断获取的网络数据是否是空
            if (responseBody == null) {
                sendProducerResult(
                    localPath, url, RequestStatus.STATUS_FAIL,
                    RequestException(
                        RequestException.CODE_ERROR_IO_EXCEPTION,
                        "make dir file fail"
                    )
                )
                close()
                return@produce
            }

            val futureTmpFile = File("$localPath.temp")
            if (!futureTmpFile.parentFile.exists() || futureTmpFile.parentFile.exists() &&
                !futureTmpFile.parentFile.isDirectory
            ) {
                if (futureTmpFile.parentFile.exists()) {
                    futureTmpFile.parentFile.delete()
                }
                val isMkSuccess: Boolean = futureTmpFile.parentFile.mkdirs()
                if (!isMkSuccess) { //处理错误
                    sendProducerResult(
                        localPath, url, RequestStatus.STATUS_FAIL,
                        RequestException(
                            RequestException.CODE_ERROR_IO_EXCEPTION,
                            "make dir file fail"
                        )
                    )
                    close()
                    return@produce
                }
            }

            if (futureTmpFile.exists()) {
                futureTmpFile.delete()
            }

            val buffer = ByteArray(2048)
            inputStream = responseBody.byteStream()
            outputStream = FileOutputStream(futureTmpFile)
            //文件总长度
            totalSize = responseBody.contentLength()
            var len: Int
            while (inputStream.read(buffer).also { len = it } != -1 && isActive) {
                outputStream.write(buffer, 0, len)
                progressSize += len
                if (isActive) { //回调下载进度
                    sendProducerResult(
                        localPath, url, RequestStatus.STATUS_LOADING, null,
                        totalSize,
                        progressSize
                    )
                } else {
                    sendProducerResult(
                        localPath, url, RequestStatus.STATUS_FAIL,
                        RequestException(
                            RequestException.CODE_ERROR_CANCELED,
                            "Canceled"
                        ),
                        totalSize, progressSize
                    )
                    close()
                    return@produce
                }
            }
            outputStream.flush()
            //判断是否已经取消
            if (!isActive) {
                sendProducerResult(
                    localPath, url, RequestStatus.STATUS_FAIL,
                    RequestException(
                        RequestException.CODE_ERROR_CANCELED,
                        "Canceled"
                    ),
                    totalSize, progressSize
                )
                close()
                return@produce
            }

            //下载完成之后temp后缀重命名成正式的文件名
            val saveFile = File(localPath)
            if (saveFile.exists()) {
                saveFile.delete()
            }
            val isRenameSuccess: Boolean = futureTmpFile.renameTo(saveFile)
            if (!isRenameSuccess) {
                sendProducerResult(
                    localPath, url, RequestStatus.STATUS_FAIL,
                    RequestException(
                        RequestException.CODE_ERROR_IO_EXCEPTION,
                        "rename file fail"
                    ),
                    totalSize, progressSize
                )
                close()
                return@produce
            }

            //完成执行成功
            sendProducerResult(
                localPath, url, RequestStatus.STATUS_SUCCESS, null,
                totalSize,
                progressSize
            )
            close()
        } catch (e: Exception) { //处理错误
            send(
                RequestResult(
                    localPath,
                    url,
                    RequestStatus.STATUS_FAIL,
                    0,
                    0,
                    RequestException.transformException(e)
                )
            )
            close()
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close()
                } catch (e: IOException) {
                    sendProducerResult(
                        localPath,
                        url,
                        RequestStatus.STATUS_FAIL,
                        RequestException.transformException(e),
                        totalSize, progressSize
                    )
                    close()
                }
            }
            if (outputStream != null) {
                try {
                    outputStream.close()
                } catch (e: IOException) {
                    sendProducerResult(
                        localPath,
                        url,
                        RequestStatus.STATUS_FAIL,
                        RequestException.transformException(e),
                        totalSize, progressSize
                    )
                    close()
                }
            }
        }
    }

    private suspend fun ProducerScope<RequestResult>.sendProducerResult(
        localPath: String,
        url: String,
        status: Int,
        exception: RequestException? = null,
        totalSize: Long = 0L,
        progressSize: Long = 0L
    ) {

        val requestResult =
            RequestResult(localPath, url, status, totalSize, progressSize, exception)
        send(requestResult)
    }
}